#include "ledgfx.h"
//#include "Adafruit_GFX.h"
//#include "twimaster.h"
//#include "light_ws2812.h"
#include <avr/io.h>
#include <stdlib.h>
/*
uint32_t eemuster_folge[len_folge] EEMEM = {14680078,15139278,473536,15139278,14680078,28311579,28869179,18383409,575008,913248,473536,913248,575008,17408,10240,4096,328000,17825809,18153809,18157905,22369621,29201275,33412991,15587182,15583086,15255086,33080895,11512810,4357252,11512810,33080895,28869179,18383409,17825809,18153809,29197179,13103046,7302636,29197179,28311579};
uint32_t eemuster_folge_2[len_folge_2] EEMEM = {14680078,15139278,473536,15139278,14680078,28311579,885600,27648,885600,28311579,28312416,28339200,29196288,912384,28512,891,27675,884763,28311579,17825809,18153809,18157905,332096,4096,145536,4357252,4685252,22511061,33554431,33550335,33550207,33549951,33541759,33279615,33148543,33083007,33080959,33080895,33080893,33080889,33080881,33080865,33080353,33063969,32539681,3115074,4357252,8659944,17318431,17318671,17326471,17576131,26097891,13331878,6407948,3179032,7340060,14680078,29360135,25690147,20988933,18907657,17825809,2621480,4211716,8421890,17825809,26247699,2949480,4353156,8749890,328000,469440,469326,15018318,15022414,33095231,32537631,14694414,14684174};
uint32_t eemuster_folge_fade[len_folge_fade] EEMEM = {14680078,459200,14336,688800,22020117,10507274,22030357,710304,10824010,22730421,10824010,22041621,11512810,4211716,8421890,17825809,2621480,4211716,8421890,17825809,2621480,12633606,26247699,2949480,4353156,8749890,20447289,12961606,28869179,7161196,17971345,4539716,141440,332096,2762920,4543812,11184810,22369621,10496010,459200};
uint32_t eemuster_bonvoyage[len_bonvoyage] EEMEM = {32641183,16405135,33182344,33182344,25304216,0,33216174,589344,33182409,17333776,32641183,33080878,16405135,25304216,0,33216174,15255086,32772191,25363672,15255086,25304216,16405135,15259319,33216181,29696,0};
uint32_t eemuster_orakel_start[len_orakel_start] EEMEM = {15255086,33182409,16405135,32649793,33216181,32539681,0,0};
uint32_t eemuster_orakel[len_orakel] EEMEM = {13182540,13582284,1030272,1016800,3276036,4488452,4291652,4674692,4347332,8380391,26189816,2132104,20287859,15716206,546560,29696,1055775,15430465,25361580,53230,16481999,2588098,2393378,1044868};
uint32_t eemuster_arrows[len_arrows] EEMEM = {4488452,1659678,4347332,17077359,4291652,15832368,4674692,32264769};
*/
/*
uint8_t red[20][20];
uint8_t green[20][20];
uint8_t blue[20][20];
*/
/*
uint8_t getred(int8_t x,int8_t x,){
	return red[x][y];
}
uint8_t getgreen(int8_t x,int8_t x,){
	return green[x][y];
}
uint8_t getblue(int8_t x,int8_t x,){
	return blue[x][y];
}
*/

/* 
  dim_curve 'lookup table' to compensate for the nonlinearity of human vision.
  Used in the getRGB function on saturation and brightness to make 'dimming' look more natural. 
  Exponential function used to create values below : 
  x from 0 - 255 : y = round(pow( 2.0, x+64/40.0) - 1)   
*/
const 	uint8_t log_umrechnung[60]={0,0,0,0,0,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,7,8,9,10,11,12,13,14,16,17,19,21,24,26,29,32,35,39,43,47,52,58,64,70,77,86,94,104,115,127,141,155,171,189,209,231,255};

const uint8_t dim_curve[] = {
    0,   1,   1,   2,   2,   2,   2,   2,   2,   3,   3,   3,   3,   3,   3,   3,
    3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,   4,   4,   4,   4,
    4,   4,   4,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   6,   6,   6,
    6,   6,   6,   6,   6,   7,   7,   7,   7,   7,   7,   7,   8,   8,   8,   8,
    8,   8,   9,   9,   9,   9,   9,   9,   10,  10,  10,  10,  10,  11,  11,  11,
    11,  11,  12,  12,  12,  12,  12,  13,  13,  13,  13,  14,  14,  14,  14,  15,
    15,  15,  16,  16,  16,  16,  17,  17,  17,  18,  18,  18,  19,  19,  19,  20,
    20,  20,  21,  21,  22,  22,  22,  23,  23,  24,  24,  25,  25,  25,  26,  26,
    27,  27,  28,  28,  29,  29,  30,  30,  31,  32,  32,  33,  33,  34,  35,  35,
    36,  36,  37,  38,  38,  39,  40,  40,  41,  42,  43,  43,  44,  45,  46,  47,
    48,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,
    63,  64,  65,  66,  68,  69,  70,  71,  73,  74,  75,  76,  78,  79,  81,  82,
    83,  85,  86,  88,  90,  91,  93,  94,  96,  98,  99,  101, 103, 105, 107, 109,
    110, 112, 114, 116, 118, 121, 123, 125, 127, 129, 132, 134, 136, 139, 141, 144,
    146, 149, 151, 154, 157, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 190,
    193, 196, 200, 203, 207, 211, 214, 218, 222, 226, 230, 234, 238, 242, 248, 255,
};





void getRGB(uint16_t hue, uint8_t sat, uint8_t val, uint8_t colors[3]) { 
  /* convert hue, saturation and brightness ( HSB/HSV ) to RGB
     The dim_curve is used only on brightness/value and on saturation (inverted).
     This looks the most natural.      
  */
  
  val = dim_curve[val];
  sat = 255-dim_curve[255-sat];
  
  int r;
  int g;
  int b;
  int base;
  
  if (sat == 0) { // Acromatic color (gray). Hue doesn't mind.
    colors[0]=val;
    colors[1]=val;
    colors[2]=val;  
  } else  { 
    
    base = ((255 - sat) * val)>>8;
  
    switch(hue/60) {
	case 0:
		r = val;
		g = (((val-base)*hue)/60)+base;
		b = base;
	break;
	
	case 1:
		r = (((val-base)*(60-(hue%60)))/60)+base;
		g = val;
		b = base;
	break;
	
	case 2:
		r = base;
		g = val;
		b = (((val-base)*(hue%60))/60)+base;
	break;
	
	case 3:
		r = base;
		g = (((val-base)*(60-(hue%60)))/60)+base;
		b = val;
	break;
	
	case 4:
		r = (((val-base)*(hue%60))/60)+base;
		g = base;
		b = val;
	break;
	
	case 5:
		r = val;
		g = base;
		b = (((val-base)*(60-(hue%60)))/60)+base;
	break;
    }
      
    colors[0]=r;
    colors[1]=g;
    colors[2]=b; 
  }   

}


uint32_t getPixel(int8_t x,int8_t y){
	//touch_status |= ((uint32_t) 1 << 31);
	uint32_t pixelcolour=((uint32_t)(led[x][y].red)<<16);
	//transmitByte(red[x][y]);
	pixelcolour|=((uint32_t)(led[x][y].green)<<8);
	pixelcolour|=((uint32_t)(led[x][y].blue));
	//transmitByte(pixelcolour);
	return pixelcolour;
}
void drawPixel(int8_t x, int8_t y, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  if ((y < 0) || (y >= _height)) return;
  if ((x < 0) || (x >= _width)) return;

 // check rotation, move pixel around if necessary
  switch (getRotation()) {
  case 1:
    swap(x, y);
    x = _width - x - 1;
    break;
  case 2:
    x = _width - x - 1;
    y = _height - y - 1;
    break;
  case 3:
    swap(x, y);
    y = _height - y - 1;
    break;
  }

  // wrap around the x
  //x += 4; // was ist das?
  //x %= 5;
if (c_red<256) led[x][y].red=c_red;
	else led[x][y].red=0;

if (c_green<256) led[x][y].green=c_green;
else led[x][y].green=0;

if (c_blue<256) led[x][y].blue=c_blue;
else led[x][y].blue=0;



  
}

void drawLine(int8_t x0, int8_t y0, int8_t x1, int8_t y1, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
	int8_t steep = abs(y1 - y0) > abs(x1 - x0);
	if (steep) {
		swap(x0, y0);
		swap(x1, y1);
	}

	if (x0 > x1) {
		swap(x0, x1);
		swap(y0, y1);
	}

	int8_t dx, dy;
	dx = x1 - x0;
	dy = abs(y1 - y0);

	int8_t err = dx / 2;
	int8_t ystep;

	if (y0 < y1) {
		ystep = 1;
	} else {
		ystep = -1;
	}

	for (; x0<=x1; x0++) {
		if (steep) {
			 drawPixel(y0, x0, c_red, c_green, c_blue);
		} else {
			 drawPixel(x0, y0, c_red, c_green, c_blue);
		}
		err -= dy;
		if (err < 0) {
			y0 += ystep;
			err += dx;
		}
	}
}


void led_out() {
	//cli();
//for (int x = 0; x<_width; x++) //wenn eindimensional
for (int x = 0; x<_width; x=x+1) //wenn eindimensional
{
		for (int y = 0; y<_height; y++)
			{//durch 4 teilen um auf wertebereich von 64 zu kommen, dann log umrechnung anwenden
// 				helligkeit_log[(x+y)*3]=log_umrechnung[(led[x][y].r>>2)];
// 				helligkeit_log[((x+y)*3)+1]=log_umrechnung[(led[x][y].g>>2)];
// 				helligkeit_log[((x+y)*3)+2]=log_umrechnung[(led[x][y].b>>2)];
//helligkeit_log[((x*_height)+y)*3]=led[x][y].r;
dmx_data[x*3]=led[x][y].r;
dmx_data[x*3+1]=led[x][y].g;
dmx_data[x*3+2]=led[x][y].b;

//helligkeit_log[(((x*_height)+y)*3)+1]=led[x][y].g;
//helligkeit_log[(((x*_height)+y)*3)+2]=led[x][y].b;
			}
		//
	//
}	

convert(pwm_data, dmx_data);
pwm_update(pwm_data);

// 		memcpy(pwm_setting, helligkeit_log, PWM_CHANNELS);
// 		pwm_update();
// 	for (int x = 0; x<_width; x++)
// 	{
// 		if ((x&1) != 1)
// 		{
// 			for (int y = 0; y<_height; y++)
// 			{
// 				ws2812_sendarray_mask((uint8_t *)&led[(_width-1)-x][(_height-1)-y],3,_BV(3));
// 			}
// 		}
// 		else{
// 			for (int y = 0; y<_height; y++)
// 			{
// 				ws2812_sendarray_mask((uint8_t *)&led[(_width-1)-x][y],3,_BV(3));
// 			}
// 		}
// 	}
	//sei();
}

// draw a circle outline
void drawCircle(int16_t x0, int16_t y0, int16_t r, 
			      uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  int16_t f = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x = 0;
  int16_t y = r;

  drawPixel(x0, y0+r, c_red, c_green, c_blue);
  drawPixel(x0, y0-r, c_red, c_green, c_blue);
  drawPixel(x0+r, y0, c_red, c_green, c_blue);
  drawPixel(x0-r, y0, c_red, c_green, c_blue);

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
  
    drawPixel(x0 + x, y0 + y, c_red, c_green, c_blue);
    drawPixel(x0 - x, y0 + y, c_red, c_green, c_blue);
    drawPixel(x0 + x, y0 - y, c_red, c_green, c_blue);
    drawPixel(x0 - x, y0 - y, c_red, c_green, c_blue);
    drawPixel(x0 + y, y0 + x, c_red, c_green, c_blue);
    drawPixel(x0 - y, y0 + x, c_red, c_green, c_blue);
    drawPixel(x0 + y, y0 - x, c_red, c_green, c_blue);
    drawPixel(x0 - y, y0 - x, c_red, c_green, c_blue);
    
  }
}
/*
void drawCircleHelper( int16_t x0, int16_t y0,
               int16_t r, uint8_t cornername, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;
    if (cornername & 0x4) {
      drawPixel(x0 + x, y0 + y, c_red, c_green, c_blue);
      drawPixel(x0 + y, y0 + x, c_red, c_green, c_blue);
    } 
    if (cornername & 0x2) {
      drawPixel(x0 + x, y0 - y, c_red, c_green, c_blue);
      drawPixel(x0 + y, y0 - x, c_red, c_green, c_blue);
    }
    if (cornername & 0x8) {
      drawPixel(x0 - y, y0 + x, c_red, c_green, c_blue);
      drawPixel(x0 - x, y0 + y, c_red, c_green, c_blue);
    }
    if (cornername & 0x1) {
      drawPixel(x0 - y, y0 - x, c_red, c_green, c_blue);
      drawPixel(x0 - x, y0 - y, c_red, c_green, c_blue);
    }
  }
}
*/
void fillCircle(int16_t x0, int16_t y0, int16_t r, 
			      uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  drawFastVLine(x0, y0-r, 2*r+1, c_red, c_green, c_blue);
  fillCircleHelper(x0, y0, r, 3, 0, c_red, c_green, c_blue);
}

// used to do circles and roundrects!
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
				    uint8_t cornername, int16_t delta, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {

  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;

    if (cornername & 0x1) {
      drawFastVLine(x0+x, y0-y, 2*y+1+delta, c_red, c_green, c_blue);
      drawFastVLine(x0+y, y0-x, 2*x+1+delta, c_red, c_green, c_blue);
    }
    if (cornername & 0x2) {
      drawFastVLine(x0-x, y0-y, 2*y+1+delta, c_red, c_green, c_blue);
      drawFastVLine(x0-y, y0-x, 2*x+1+delta, c_red, c_green, c_blue);
    }
  }
}

// bresenham's algorithm - thx wikpedia


// draw a rectangle
void drawRect(int16_t x, int16_t y, 
			    int16_t w, int16_t h, 
			    uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  drawFastHLine(x, y, w, c_red, c_green, c_blue);
  drawFastHLine(x, y+h-1, w, c_red, c_green, c_blue);
  drawFastVLine(x, y, h, c_red, c_green, c_blue);
  drawFastVLine(x+w-1, y, h, c_red, c_green, c_blue);
}

void drawFastVLine(int16_t x, int16_t y, 
				 int16_t h, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  // stupidest version - update in subclasses if desired!
  drawLine(x, y, x, y+h-1, c_red, c_green, c_blue);
}


void drawFastHLine(int16_t x, int16_t y, 
				 int16_t w, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  // stupidest version - update in subclasses if desired!
  drawLine(x, y, x+w-1, y, c_red, c_green, c_blue);
}

void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, 
			    uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  // stupidest version - update in subclasses if desired!
  for (int16_t i=x; i<x+w; i++) {
    drawFastVLine(i, y, h, c_red, c_green, c_blue); 
  }
}


void fillScreen(uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  fillRect(0, 0, _width, _height, c_red, c_green, c_blue);
}
/*
// draw a rounded rectangle!
void drawRoundRect(int16_t x, int16_t y, int16_t w,
  int16_t h, int16_t r, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  // smarter version
  drawFastHLine(x+r  , y    , w-2*r, c_red, c_green, c_blue); // Top
  drawFastHLine(x+r  , y+h-1, w-2*r, c_red, c_green, c_blue); // Bottom
  drawFastVLine(  x    , y+r  , h-2*r, c_red, c_green, c_blue); // Left
  drawFastVLine(  x+w-1, y+r  , h-2*r, c_red, c_green, c_blue); // Right
  // draw four corners
  drawCircleHelper(x+r    , y+r    , r, 1, c_red, c_green, c_blue);
  drawCircleHelper(x+w-r-1, y+r    , r, 2, c_red, c_green, c_blue);
  drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, c_red, c_green, c_blue);
  drawCircleHelper(x+r    , y+h-r-1, r, 8, c_red, c_green, c_blue);
}
*/
/*
// fill a rounded rectangle!
void fillRoundRect(int16_t x, int16_t y, int16_t w,
				 int16_t h, int16_t r, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  // smarter version
  fillRect(x+r, y, w-2*r, h, c_red, c_green, c_blue);

  // draw four corners
  fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, c_red, c_green, c_blue);
  fillCircleHelper(x+r    , y+r, r, 2, h-2*r-1, c_red, c_green, c_blue);
}
*/
/*
// draw a triangle!
void drawTriangle(int16_t x0, int16_t y0,
				int16_t x1, int16_t y1, 
				int16_t x2, int16_t y2, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {
  drawLine(x0, y0, x1, y1, c_red, c_green, c_blue);
  drawLine(x1, y1, x2, y2, c_red, c_green, c_blue);
  drawLine(x2, y2, x0, y0, c_red, c_green, c_blue);
}

// fill a triangle!
void fillTriangle ( int16_t x0, int16_t y0,
				  int16_t x1, int16_t y1, 
				  int16_t x2, int16_t y2, uint8_t c_red, uint8_t c_green, uint8_t c_blue) {

  int16_t a, b, y, last;

  // Sort coordinates by Y order (y2 >= y1 >= y0)
  if (y0 > y1) {
    swap(y0, y1); swap(x0, x1);
  }
  if (y1 > y2) {
    swap(y2, y1); swap(x2, x1);
  }
  if (y0 > y1) {
    swap(y0, y1); swap(x0, x1);
  }

  if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
    a = b = x0;
    if(x1 < a)      a = x1;
    else if(x1 > b) b = x1;
    if(x2 < a)      a = x2;
    else if(x2 > b) b = x2;
    drawFastHLine(a, y0, b-a+1, c_red, c_green, c_blue);
    return;
  }

  int16_t
    dx01 = x1 - x0,
    dy01 = y1 - y0,
    dx02 = x2 - x0,
    dy02 = y2 - y0,
    dx12 = x2 - x1,
    dy12 = y2 - y1,
    sa   = 0,
    sb   = 0;

  // For upper part of triangle, find scanline crossings for segments
  // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
  // is included here (and second loop will be skipped, avoiding a /0
  // error there), otherwise scanline y1 is skipped here and handled
  // in the second loop...which also avoids a /0 error here if y0=y1
  // (flat-topped triangle).
  if(y1 == y2) last = y1;   // Include y1 scanline
  else         last = y1-1; // Skip it

  for(y=y0; y<=last; y++) {
    a   = x0 + sa / dy01;
    b   = x0 + sb / dy02;
    sa += dx01;
    sb += dx02;
    //longhand:
    //a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
    //b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    
    if(a > b) swap(a,b);
    drawFastHLine(a, y, b-a+1, c_red, c_green, c_blue);
  }

  // For lower part of triangle, find scanline crossings for segments
  // 0-2 and 1-2.  This loop is skipped if y1=y2.
  sa = dx12 * (y - y1);
  sb = dx02 * (y - y0);
  for(; y<=y2; y++) {
    a   = x1 + sa / dy12;
    b   = x0 + sb / dy02;
    sa += dx12;
    sb += dx02;
    // longhand:
    //a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
    //b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    
    if(a > b) swap(a,b);
    drawFastHLine(a, y, b-a+1, c_red, c_green, c_blue);
  }
}
*/
/*
void drawBitmap(int16_t x, int16_t y, 
			      const uint8_t *bitmap, int16_t w, int16_t h,
			      uint8_t c_red, uint8_t c_green, uint8_t c_blue) {

  int16_t i, j, byteWidth = (w + 7) / 8;

  for(j=0; j<h; j++) {
    for(i=0; i<w; i++ ) {
      if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
	drawPixel(x+i, y+j, c_red, c_green, c_blue);
      }
    }
  }
}
*/
/*
#if ARDUINO >= 100
size_t write(uint8_t c) {
#else
void write(uint8_t c) {
#endif
  if (c == '\n') {
    cursor_y += textsize*8;
    cursor_x = 0;
  } else if (c == '\r') {
    // skip em
  } else {
    drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize);
    cursor_x += textsize*6;
    if (wrap && (cursor_x > (_width - textsize*6))) {
      cursor_y += textsize*8;
      cursor_x = 0;
    }
  }
#if ARDUINO >= 100
  return 1;
#endif
}
*/

// draw a character
void drawChar(int8_t x_offset, uint32_t letter, uint8_t c_red, uint8_t c_green, uint8_t c_blue)
{
	
	uint32_t ct_led=1;
	
	//transmitByte(c);

	for(uint8_t x=0;x<5;x++)
	{
		for(uint8_t y=0;y<5;y++)
		{
			if ((letter&ct_led)){
				drawPixel(x + x_offset, y, c_red, c_green, c_blue);
			}
			ct_led=ct_led<<1;
		}
	}
}   
uint8_t getRotation(void) {
  rotation %= 4;
  return rotation;
}


void setRotation(uint8_t x) {
  x %= 4;  // cant be higher than 3
  rotation = x;
  /*switch (x) {
  case 0:
  case 2:
    _width = WIDTH;
    _height = HEIGHT;
    break;
  case 1:
  case 3:
    _width = HEIGHT;
    _height = WIDTH;
    break;
	
  }*/
}


void invertDisplay() {
  for (uint8_t x=0;x<5;x++)
  {
	  for (uint8_t y=0;y<5;y++)
	  {
  
   
		  led[x][y].red=255-led[x][y].red;
		  led[x][y].green=255-led[x][y].green;
		  led[x][y].blue=255-led[x][y].blue;
	  }
  }			
}

/*
// return the size of the display which depends on the rotation!
int16_t width(void) { 
  return _width; 
}
 
int16_t height(void) { 
  return _height; 
}
*/